#include "sonic.h"
#include "wave.h"
#include <corecrt_wstdio.h>
#include <cstdio>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFFER_SIZE 2048
#define READ_BUFFER_SIZE 4096



/**
inFileName:输入文件路径
outFileName:输出文件路径
speed：调整播放的倍数
*/
static void runSonic(const char *inFileName, const char *outFileName, float speed, float pitch, float rate,
                     float volume, int outputSampleRate, int emulateChordPitch, int quality,
                     int computeSpectrogram = 0) {
  waveFile inFile, outFile = NULL;
  sonicStream stream;
  short inBuffer[BUFFER_SIZE], outBuffer[BUFFER_SIZE];
  int sampleRate, numChannels, samplesRead, samplesWritten;

  inFile = openInputWaveFile(inFileName, &sampleRate, &numChannels);
  if (outputSampleRate != 0) {
    sampleRate = outputSampleRate;
  }
  if (inFile == NULL) {
    fprintf(stderr, "Unable to read wave file %s\n", inFileName);
    exit(1);
  }
  if (!computeSpectrogram) {
    outFile = openOutputWaveFile(outFileName, sampleRate, numChannels);
    if (outFile == NULL) {
      closeWaveFile(inFile);
      fprintf(stderr, "Unable to open wave file %s for writing\n", outFileName);
      exit(1);
    }
  }
  stream = sonicCreateStream(sampleRate, numChannels);
  sonicSetSpeed(stream, speed);
  sonicSetPitch(stream, pitch);
  sonicSetRate(stream, rate);
  sonicSetVolume(stream, volume);
  sonicSetChordPitch(stream, emulateChordPitch);
  sonicSetQuality(stream, quality);
  do {
    samplesRead = readFromWaveFile(inFile, inBuffer, BUFFER_SIZE / numChannels);
    if (samplesRead == 0) {
      sonicFlushStream(stream);
    } else {
      sonicWriteShortToStream(stream, inBuffer, samplesRead);
    }
    if (!computeSpectrogram) {
      do {
        samplesWritten = sonicReadShortFromStream(stream, outBuffer, BUFFER_SIZE / numChannels);
        if (samplesWritten > 0 && !computeSpectrogram) {
          writeToWaveFile(outFile, outBuffer, samplesWritten);
        }
      } while (samplesWritten > 0);
    }
  } while (samplesRead > 0);

  sonicDestroyStream(stream);
  closeWaveFile(inFile);
  if (!computeSpectrogram) {
    closeWaveFile(outFile);
  }
}


struct PCMFile {
  const char *name;
  int sampleRate;
  int numChannels;
};

/**
  buffer:用于存放读取采样点的缓存
  maxSamples：一个通道最多读取多少个采样点
  channels:通道数
  return:返回实际读取的采样点
*/
int readSamplesFromPCMFile(FILE *pcmFile, short *buffer, int maxSamples, int channels) {
  unsigned char buf[READ_BUFFER_SIZE];
  if (maxSamples * channels * 2 > READ_BUFFER_SIZE) {
    maxSamples = READ_BUFFER_SIZE / (channels << 1);
  }
  int read_bytes = fread(buf, sizeof(char), maxSamples * channels * 2, pcmFile);
  int samplesRead = read_bytes / (channels << 1);

  int bytePos = 0;
  for (int i = 0; i < samplesRead * channels; i++) {
    short sample = buf[bytePos++];
    sample |= (unsigned int)buf[bytePos++] << 8;
    *buffer++ = sample;
  }
  return samplesRead;
}

/**
  test.pcm:48000,2声道,16位
*/
void runSonicPCM(const PCMFile &inputPCM, const char *outFileName, float speed, float pitch, float rate, float volume,
                 int outputSampleRate, int emulateChordPitch, int quality) {
  FILE *inFile = nullptr;

  // 打开输入文件
  errno_t error = fopen_s(&inFile, inputPCM.name, "rb+");
  if (error != 0) {
    fprintf(stderr, "Unable to open pcn file %s\n", inputPCM.name);
    return;
  }
  printf("open input file:%s success\n", inputPCM.name);

  int sampleRate = outputSampleRate == 0 ? inputPCM.sampleRate : outputSampleRate;
  // 打开输出文件
  waveFile outFile = openOutputWaveFile(outFileName, sampleRate, inputPCM.numChannels);
  if (error != 0) {
    fclose(inFile);
    fprintf(stderr, "Unable to open wave file %s for writing\n", outFileName);
    exit(1);
  }
  printf("open output file:%s success.\n", outFileName);

  sonicStream stream = sonicCreateStream(sampleRate, inputPCM.numChannels);
  sonicSetSpeed(stream, speed);
  sonicSetPitch(stream, pitch);
  sonicSetRate(stream, rate);
  sonicSetVolume(stream, volume);
  sonicSetChordPitch(stream, emulateChordPitch);
  sonicSetQuality(stream, quality);

  int samplesRead = 0, samplesWritten = 0;
  short inBuffer[BUFFER_SIZE], outBuffer[BUFFER_SIZE];

  do {
    samplesRead = readSamplesFromPCMFile(inFile, inBuffer, BUFFER_SIZE / inputPCM.numChannels, inputPCM.numChannels);
    if (samplesRead == 0) {
      sonicFlushStream(stream);
    } else {
      sonicWriteShortToStream(stream, inBuffer, samplesRead);
    }

    do {
      samplesWritten = sonicReadShortFromStream(stream, outBuffer, BUFFER_SIZE / inputPCM.numChannels);
      if (samplesWritten > 0) {
        printf("samplesWrite:%d\n", samplesWritten);
        writeToWaveFile(outFile, outBuffer, samplesWritten);
      }
    } while (samplesWritten > 0);

  } while (samplesRead > 0);

  sonicDestroyStream(stream);
  fclose(inFile);
  closeWaveFile(outFile);
}



int main() {
  PCMFile pcmInputFile{
    .name = "res/test.pcm", 
    .sampleRate = 48000, 
    .numChannels = 2};
  
  const char *outputFileName = "res/test_1_3x.wav";

  runSonicPCM(pcmInputFile, outputFileName, 1.3f, 1.0, 1.0, 1.0, 0, 0, 0);
  return 0;
}